package cn.rocksolid.sport.security.service.impl;

import cn.rocksolid.sport.common.C;
import cn.rocksolid.sport.common.Constant.St;
import cn.rocksolid.sport.common.error.RSE;
import cn.rocksolid.sport.common.utils.AssertUtils;
import cn.rocksolid.sport.common.utils.QwUtils;
import cn.rocksolid.sport.security.dao.UserDao;
import cn.rocksolid.sport.security.dao.UserRoleDao;
import cn.rocksolid.sport.security.entity.MenuEntity;
import cn.rocksolid.sport.security.entity.RoleEntity;
import cn.rocksolid.sport.security.entity.UserEntity;
import cn.rocksolid.sport.security.entity.UserRoleEntity;
import cn.rocksolid.sport.security.service.MenuService;
import cn.rocksolid.sport.security.service.RoleService;
import cn.rocksolid.sport.security.service.UserService;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * System User service implementation
 *
 * @author Axl Zhao
 * @email axl.zhao@163.com
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements UserService {

  private final UserRoleDao userRoleDao;
  private final MenuService menuService;
  private final RoleService roleService;

  @Autowired
  public UserServiceImpl(final MenuService menuService, final UserRoleDao userRoleDao, final RoleService roleService) {
    this.userRoleDao = userRoleDao;
    this.menuService = menuService;
    this.roleService = roleService;
  }

  @Override
  public UserEntity exist(final UserEntity userEntity) {
    List<UserEntity> exists = this.list(
            new QueryWrapper<UserEntity>().ne(StringUtils.isNotBlank(userEntity.getId()), C.ID.f, userEntity.getId())
                                          .and(qw -> qw.eq(C.USERNAME.f, userEntity.getUsername()).or()
                                                       .eq(C.MOBILE.f, userEntity.getMobile()).or()
                                                       .eq(C.EMAIL.f, userEntity.getEmail())));
    return CollectionUtils.isEmpty(exists) ? null : exists.get(0);
  }

  @Override
  public UserEntity get(final String userId) {
    UserEntity userEntity = this.getById(userId);
    desensitization(userEntity);
    if (null != userEntity) {
      Collection<String> roleIds = CollectionUtils.collect(
              userRoleDao.selectList(new QueryWrapper<UserRoleEntity>().eq(C.USER_ID.f, userId)),
              UserRoleEntity::getRoleId);
      if (CollectionUtils.isNotEmpty(roleIds)) {
        userEntity.setSysRoles(roleService.list(new QueryWrapper<RoleEntity>().in("id", roleIds)));
      }
    }
    return userEntity;
  }

  @Override
  public IPage<UserEntity> paging(final IPage<UserEntity> page, final JSONObject cond) {
    QueryWrapper<UserEntity> qw = QwUtils.fuzzy(cond, C.NAME.f, C.USERNAME.f, C.EMAIL.f, C.MOBILE.f);
    qw.select("insert(mobile, 4, 4, '****') as mobile", "t_user.*");
    IPage<UserEntity> pr = this.page(page, qw);
    for (UserEntity sysUser : CollectionUtils.emptyIfNull(pr.getRecords())) {
      desensitization(sysUser);
    }
    return pr;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void refreshAdminPerms(final UserEntity userEntity) {
    RoleEntity roleEntity = new RoleEntity();
    roleEntity.setTitle(userEntity.getName());
    roleEntity.setSt(St.AVAILABLE);
    roleService.save(roleEntity);
    List<MenuEntity> allMenus = menuService.list(new QueryWrapper<>());
    roleService.refreshMenus(roleEntity.getId(), CollectionUtils.collect(allMenus, MenuEntity::getId));
    refreshRoles(userEntity.getId(), Collections.singletonList(roleEntity.getId()));
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void create(final UserEntity userEntity) {
    desensitization(userEntity);
    this.save(userEntity);
    refreshRoles(userEntity.getId(), CollectionUtils.collect(userEntity.getSysRoles(), RoleEntity::getId));
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void modify(final UserEntity userEntity) {
    desensitization(userEntity);
    this.updateById(userEntity);
    refreshRoles(userEntity.getId(), CollectionUtils.collect(userEntity.getSysRoles(), RoleEntity::getId));
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void removes(final Collection<String> uids) {
    if (CollectionUtils.isNotEmpty(uids)) {
      this.removeByIds(uids);
      userRoleDao.delete(new UpdateWrapper<UserRoleEntity>().in(C.USER_ID.f, uids));
    }
  }

  private UserEntity getSysUserByLoginId(final String loginId) {
    UserEntity user = this.getOne(
            new QueryWrapper<UserEntity>().eq(C.USERNAME.f, loginId).or().eq(C.EMAIL.f, loginId).or()
                                          .eq(C.MOBILE.f, loginId));
    AssertUtils.isNotNull(user, RSE.U_NOTFOUND);
    return user;
  }

  private void refreshRoles(final String userId, final Collection<String> rids) {
    // Delete user and role relations
    userRoleDao.delete(new UpdateWrapper<UserRoleEntity>().eq(C.USER_ID.f, userId));
    if (CollectionUtils.isEmpty(rids)) {
      return;
    }
    // Save new relations between the user and role
    for (String roleId : rids) {
      UserRoleEntity userRoleEntity = new UserRoleEntity();
      userRoleEntity.setUserId(userId);
      userRoleEntity.setRoleId(roleId);
      userRoleDao.insert(userRoleEntity);
    }
  }

  private void desensitization(UserEntity userEntity) {
    if (null == userEntity) {
      return;
    }
    //    sysUserEntity.setSalt(null);
    //    sysUserEntity.setPassword(null);
  }

}
